/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "ChromeClientManx.h"

#include "Console.h"
#include "FileChooser.h"
#include "FloatRect.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameView.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLTextAreaElement.h"
#include "HitTestResult.h"
#include "NamedNodeMap.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PopupMenuManx.h"
#include "SearchPopupMenuManx.h"
#include "WebViewPopupMenu.h"
#include "WebViewPrivate.h"
#include "WindowFeatures.h"
#include <stdio.h>
#include <webkit/WebView.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>

// #define MYTRACE    printf("+++ ChromeClientManx::%s() +++ :L%d\n", __FUNCTION__, __LINE__);
#define MYTRACE

namespace WebCore {

ChromeClientManx::ChromeClientManx(WebKit::WebViewPrivate* webView)
    : m_listener(0)
{
    m_webView = webView;
}

ChromeClientManx::~ChromeClientManx()
{
}

void ChromeClientManx::chromeDestroyed()
{
    MYTRACE;
    delete this;
}

void ChromeClientManx::setWindowRect(const FloatRect&)
{
    MYTRACE;
    notImplemented();
}

FloatRect ChromeClientManx::windowRect()
{
    MYTRACE;
    notImplemented();
    WebCore::IntSize size = m_webView->m_viewSize;
    return FloatRect(0, 0, size.width(), size.height());
}

FloatRect ChromeClientManx::pageRect()
{
    MYTRACE;
    notImplemented();
    return FloatRect();
}

void ChromeClientManx::focus()
{
    MYTRACE;
    notImplemented();
}

void ChromeClientManx::unfocus()
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::canTakeFocus(FocusDirection)
{
    MYTRACE;
    // This is called when cycling through links/focusable objects and we
    // reach the last focusable object. Then we want to claim that we can
    // take the focus to avoid wrapping.
    return false;
}

void ChromeClientManx::takeFocus(FocusDirection)
{
    MYTRACE;
    notImplemented();
}

void ChromeClientManx::focusedNodeChanged(Node* node)
{
    MYTRACE;
    m_webView->client().didFocusedNodeChanged();
}

void ChromeClientManx::focusedFrameChanged(Frame*)
{
    MYTRACE;
    notImplemented();
}

Page* ChromeClientManx::createWindow(Frame* frame, const FrameLoadRequest& request, const WindowFeatures& features, const NavigationAction& action)
{
    MYTRACE;
    CString url = request.resourceRequest().url().string().utf8();
    WebKit::WebView* webview = m_webView->client().createWindow(url.data(), (int)features.width, (int)features.height, true);
    if (!webview)
        return 0;
    WebKit::WebViewPrivate* webViewPriv = static_cast<WebKit::WebViewPrivate*>(webview);
    // set useragent of parent webview.
    webViewPriv->setUserAgent(m_webView->userAgent());
    return webViewPriv->m_page.get();
}

Page* ChromeClientManx::createModalDialog(Frame*, const FrameLoadRequest&)
{
    MYTRACE;
    notImplemented();
    return 0;
}

void ChromeClientManx::show()
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::canRunModal()
{
    MYTRACE;
    notImplemented();
    return false;
}

void ChromeClientManx::runModal()
{
    MYTRACE;
    notImplemented();
}

void ChromeClientManx::setToolbarsVisible(bool)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::toolbarsVisible()
{
    MYTRACE;
    notImplemented();
    return false;
}

void ChromeClientManx::setStatusbarVisible(bool)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::statusbarVisible()
{
    MYTRACE;
    notImplemented();
    return false;
}

void ChromeClientManx::setScrollbarsVisible(bool)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::scrollbarsVisible()
{
    MYTRACE;
    notImplemented();
    return false;
}

void ChromeClientManx::setMenubarVisible(bool)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::menubarVisible()
{
    MYTRACE;
    notImplemented();
    return false;
}

void ChromeClientManx::createSelectPopup(PopupMenuClient* client, int index, const IntRect& rect)
{
    MYTRACE;
    m_webView->m_popupMenu->show(client, index, rect);
}

bool ChromeClientManx::destroySelectPopup()
{
    MYTRACE;
    m_webView->m_popupMenu->hide(true);

    return true;
}

void ChromeClientManx::setResizable(bool)
{
    MYTRACE;
    notImplemented();
}

void ChromeClientManx::addMessageToConsole(MessageSource source,
    MessageLevel level,
    const String& message,
    unsigned lineNumber,
    unsigned columnNumber,
    const String& sourceID)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::canRunBeforeUnloadConfirmPanel()
{
    MYTRACE;
    notImplemented();
    return true;
}

bool ChromeClientManx::runBeforeUnloadConfirmPanel(const String& string,
    Frame* frame)
{
    MYTRACE;
    notImplemented();
    return true;
}

void ChromeClientManx::closeWindowSoon()
{
    MYTRACE;
    if (m_webView && m_webView->m_page)
        m_webView->m_page->mainFrame()->loader()->stopAllLoaders();
    m_webView->client().closeWindowSoon();
}

void ChromeClientManx::runJavaScriptAlert(Frame* frame, const String& string)
{
    MYTRACE;
    m_webView->client().runJavaScriptAlert(string);
}

bool ChromeClientManx::runJavaScriptConfirm(Frame* frame, const String& string)
{
    MYTRACE;
    return m_webView->client().runJavaScriptConfirm(string);
}

bool ChromeClientManx::runJavaScriptPrompt(Frame* frame, const String& message, const String& defaultValue, String& result)
{
    MYTRACE;
    WebKit::WebString webResult;
    bool ok = m_webView->client().runJavaScriptPrompt(message, defaultValue, webResult);
    result = webResult;
    return ok;
}

void ChromeClientManx::setStatusbarText(const String&)
{
    MYTRACE;
    notImplemented();
}

bool ChromeClientManx::shouldInterruptJavaScript()
{
    MYTRACE;
    notImplemented();
    return false;
}

KeyboardUIMode ChromeClientManx::keyboardUIMode()
{
    MYTRACE;
    bool tabsToLinks = true;
    return tabsToLinks ? KeyboardAccessTabsToLinks : KeyboardAccessDefault;
}

bool ChromeClientManx::tabsToLinks() const
{
    MYTRACE;
    return true;
}

IntRect ChromeClientManx::windowResizerRect() const
{
    MYTRACE;
    notImplemented();
    return IntRect();
}

void ChromeClientManx::invalidateRootView(const IntRect&, bool)
{
    m_webView->invalidate(IntRect(), true);
}

void ChromeClientManx::invalidateContentsAndRootView(const IntRect& updateRect, bool immediate)
{
    m_webView->invalidate(updateRect, immediate);
}

void ChromeClientManx::invalidateContentsForSlowScroll(const IntRect& updateRect, bool immediate)
{
    m_webView->invalidate(updateRect, immediate);
}

void ChromeClientManx::repaint(const IntRect& rect, bool contentChanged, bool immediate, bool repaintContentOnly)
{
    notImplemented();
}

void ChromeClientManx::scroll(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
{
}

IntPoint ChromeClientManx::screenToRootView(const IntPoint& point) const
{
    notImplemented();
    return point;
}

IntRect ChromeClientManx::rootViewToScreen(const IntRect& rect) const
{
    notImplemented();
    return rect;
}

PlatformPageClient ChromeClientManx::platformPageClient() const
{
    return m_webView;
}

void ChromeClientManx::dispatchViewportPropertiesDidChange(const ViewportArguments& arguments) const
{
#define PRINTF(...)
// #define PRINTF printf

    PRINTF("### dispatchViewportPropertiesDidChange() ###\n");
    PRINTF("--- ViewportArguments ---\n");
    PRINTF(" type %s\n",  arguments.type == ViewportArguments::Implicit ? "Implicit" : "ViewportMeta");
    PRINTF(" width       %.1f\n", arguments.width);
    PRINTF(" minWidth    %.1f\n", arguments.minWidth);
    PRINTF(" maxWidth    %.1f\n", arguments.maxWidth);
    PRINTF(" height      %.1f\n", arguments.height);
    PRINTF(" minHeight   %.1f\n", arguments.minHeight);
    PRINTF(" maxHeight   %.1f\n", arguments.maxHeight);
    PRINTF(" zoom        %.1f\n", arguments.zoom);
    PRINTF(" minZoom     %.1f\n", arguments.minZoom);
    PRINTF(" maxZoom     %.1f\n", arguments.maxZoom);
    PRINTF(" userZoom    %.1f\n", arguments.userZoom);
    PRINTF(" orientation %.1f\n", arguments.orientation);
    PRINTF("---\n");

    WebCore::Frame *frame = m_webView->m_frame;
    if (!frame || !frame->page())
        return;

    // Zero value implies a parse error, or if not, it is out of range. Ignore such viewport meta tags.
    if (!arguments.width || !arguments.height)
        return;

    IntSize fixedLayoutSize = frame->view()->fixedLayoutSize();
    if (fixedLayoutSize.isEmpty())
        return;

    int desktopWidth = m_webView->m_defaultLayoutWidth;
    IntSize deviceSize = m_webView->m_viewSize;
    float devicePixelRatio = 1;

    PRINTF("--- Device settings ---\n");
    PRINTF(" deviceSize      (%d, %d)\n", deviceSize.width(), deviceSize.height());
    PRINTF(" desktopWidth     %d\n", desktopWidth);
    PRINTF(" devicePixelRatio %f\n", devicePixelRatio);
    PRINTF("---\n");

    // Call the common viewport computing logic in ViewportArguments.cpp.
    ViewportAttributes computed = computeViewportAttributes(
        arguments, desktopWidth, deviceSize.width(), deviceSize.height(),
        devicePixelRatio, deviceSize);
    PRINTF("=== computeViewportAttributes() ===\n");
    PRINTF("--- ComputedViewportAttributes ---\n");
    PRINTF(" width        %.1f\n", computed.layoutSize.width());
    PRINTF(" height       %.1f\n", computed.layoutSize.height());
    PRINTF(" initialScale %f\n", computed.initialScale);
    PRINTF(" maximumScale %f\n", computed.maximumScale);
    PRINTF(" minimumScale %f\n", computed.minimumScale);
    PRINTF(" userScalable %.1f\n", computed.userScalable);
    PRINTF(" orientation  %.1f\n", computed.orientation);
    PRINTF("---\n");

    restrictMinimumScaleFactorToViewportSize(computed, deviceSize, devicePixelRatio);

    PRINTF("=== restrictMinimumScaleFactorToViewportSize() ===\n");
    PRINTF("--- ComputedViewportAttributes ---\n");
    PRINTF(" width        %.1f\n", computed.layoutSize.width());
    PRINTF(" height       %.1f\n", computed.layoutSize.height());
    PRINTF(" initialScale %f\n", computed.initialScale);
    PRINTF(" maximumScale %f\n", computed.maximumScale);
    PRINTF(" minimumScale %f\n", computed.minimumScale);
    PRINTF(" userScalable %.1f\n", computed.userScalable);
    PRINTF(" orientation  %.1f\n", computed.orientation);
    PRINTF("---\n");

    restrictScaleFactorToInitialScaleIfNotUserScalable(computed);

    PRINTF("=== restrictScaleFactorToInitialScaleIfNotUserScalable() ===\n");
    PRINTF("--- ComputedViewportAttributes ---\n");
    PRINTF(" width        %.1f\n", computed.layoutSize.width());
    PRINTF(" height       %.1f\n", computed.layoutSize.height());
    PRINTF(" initialScale %f\n", computed.initialScale);
    PRINTF(" maximumScale %f\n", computed.maximumScale);
    PRINTF(" minimumScale %f\n", computed.minimumScale);
    PRINTF(" userScalable %.1f\n", computed.userScalable);
    PRINTF(" orientation  %.1f\n", computed.orientation);
    PRINTF("---\n");

    m_webView->m_viewportAttr = computed;

    frame->view()->setFixedLayoutSize(flooredIntSize(computed.layoutSize));

    IntRect actualVisibleRect = m_webView->m_frame->view()->visibleContentRect();
    IntSize visibleSize(computed.layoutSize.width(), computed.layoutSize.height());
    actualVisibleRect.setSize(visibleSize);
    m_webView->m_frame->view()->setFixedVisibleContentRect(actualVisibleRect);

    m_webView->client().didViewportChanged(computed.initialScale, computed.maximumScale, computed.minimumScale, computed.userScalable);
}

void ChromeClientManx::contentsSizeChanged(Frame* frame, const IntSize& size) const
{
    MYTRACE;
    // Ignore child frame.
    if (frame != m_webView->m_frame)
        return;

    int width = size.width();
    int height = size.height();

    // Update view's contents size since it may have changed.
    m_webView->m_contentsWidth = width;
    m_webView->m_contentsHeight = height;

#if USE(ACCELERATED_COMPOSITING)
    m_webView->m_acceleratedCompositingContext->resizeRootLayer(size);
#endif

    PRINTF("### contentsSizeChanged (%d, %d) ###\n", width, height);
    m_webView->client().didContentSizeChanged(width, height);
}

void ChromeClientManx::scrollBackingStore(int dx, int dy,
    const IntRect& scrollViewRect,
    const IntRect& clipRect)
{
    notImplemented();
}

void ChromeClientManx::updateBackingStore()
{
    notImplemented();
}

void ChromeClientManx::mouseDidMoveOverElement(const HitTestResult& hit, unsigned modifierFlags)
{
}

void ChromeClientManx::setToolTip(const String& tip, TextDirection)
{
    notImplemented();
}

void ChromeClientManx::print(Frame*)
{
    notImplemented();
}

#if ENABLE(SQL_DATABASE)
void ChromeClientManx::exceededDatabaseQuota(Frame*, const String&, DatabaseDetails)
{
    notImplemented();
}
#endif

void ChromeClientManx::reachedMaxAppCacheSize(int64_t spaceNeeded)
{
    notImplemented();
}

void ChromeClientManx::reachedApplicationCacheOriginQuota(SecurityOrigin*, int64_t)
{
    notImplemented();
}

#if USE(ACCELERATED_COMPOSITING)
void ChromeClientManx::attachRootGraphicsLayer(Frame* frame, GraphicsLayer* rootLayer)
{
    WebKit::AcceleratedCompositingContext* context = m_webView->m_acceleratedCompositingContext.get();
    bool turningOffCompositing = !rootLayer && context->enabled();
    bool turningOnCompositing = rootLayer && !context->enabled();

    context->setRootCompositingLayer(rootLayer);

    if (turningOnCompositing)
        m_webView->releaseTiles();

    if (turningOffCompositing)
        notImplemented();
}

void ChromeClientManx::setNeedsOneShotDrawingSynchronization()
{
    WebKit::AcceleratedCompositingContext* context = m_webView->m_acceleratedCompositingContext.get();
    context->scheduleLayerFlush();
}

void ChromeClientManx::scheduleCompositingLayerFlush()
{
    WebKit::AcceleratedCompositingContext* context = m_webView->m_acceleratedCompositingContext.get();
    context->scheduleLayerFlush();
}
#endif

void ChromeClientManx::runOpenPanel(Frame*, PassRefPtr<FileChooser> file)
{
    m_file = file;
    m_webView->client().runOpenPanel();
}

void ChromeClientManx::loadIconForFiles(const Vector<String>&, FileIconLoader*)
{
    notImplemented();
}

void ChromeClientManx::setCursor(const WebCore::Cursor& cursor)
{
    m_webView->client().setCursor(cursor.type());
}

void ChromeClientManx::setCursorHiddenUntilMouseMoves(bool)
{
    notImplemented();
}

#if ENABLE(TOUCH_EVENTS)
void ChromeClientManx::needTouchEvents(bool)
{
    notImplemented();
}
#endif

bool ChromeClientManx::selectItemWritingDirectionIsNatural()
{
    return true;
}

bool ChromeClientManx::selectItemAlignmentFollowsMenuWritingDirection()
{
    return false;
}

bool ChromeClientManx::hasOpenedPopup() const
{
    notImplemented();
    return false;
}

PassRefPtr<PopupMenu> ChromeClientManx::createPopupMenu(PopupMenuClient* client) const
{
    return adoptRef(new PopupMenuManx(client));
}

PassRefPtr<SearchPopupMenu> ChromeClientManx::createSearchPopupMenu(PopupMenuClient* client) const
{
    return adoptRef(new SearchPopupMenuManx(client));
}

void ChromeClientManx::setUpload(const char* path)
{
    if (m_file)
        m_file->chooseFile(String(path));
}

void ChromeClientManx::elementDidFocus(const WebCore::Node* node)
{
    MYTRACE;
#if 0
    if (node) {
        printf("   node=(type:%d)'%s'\n",
            node->nodeType(),
            node->nodeName().utf8().data());
    }
#endif
}

void ChromeClientManx::elementDidBlur(const Node* node)
{
    MYTRACE;
#if 0
    if (node) {
        printf("   node=(type:%d)'%s'\n",
            node->nodeType(),
            node->nodeName().utf8().data());
    }
#endif
}

#if USE(TILED_BACKING_STORE)
void ChromeClientManx::delegatedScrollRequested(const IntPoint& point)
{
    m_webView->client().delegatedScrollRequested(point);
}

IntRect ChromeClientManx::visibleRectForTiledBackingStore() const
{
    return m_webView->m_frame->view()->visibleContentRect();
}
#endif

}

